#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMessageBox>
#include "math.h"
#include "stdio.h"
#include "windows.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);


   // ui->pushButton_2->setHidden(true);
    //ui->label_3->setHidden(true);
    //ui->spinBox->setHidden(true);
    difference = new QCustomPlot();
    difference->setMinimumSize(300,300);
    difference->setWindowTitle("fi_omega");
    difference->show();

    phasePortrait = new QCustomPlot();
    phasePortrait->setWindowTitle("v_vDerivative");
    phasePortrait->setMinimumSize(300,300);

    x_vDer = new QCustomPlot();
    x_vDer->setWindowTitle("Three graphs");
    x_vDer->setMinimumSize(300,300);

    table = new QTableWidget(0);
    splineTable = new QTableWidget(0);

    connect(ui->doubleSpinBox_b,SIGNAL(valueChanged(double)), this, SLOT(updateStability()));
    connect(ui->doubleSpinBox_k,SIGNAL(valueChanged(double)), this, SLOT(updateStability()));
    connect(ui->doubleSpinBox_n,SIGNAL(valueChanged(double)), this, SLOT(updateStability()));
    connect(ui->doubleSpinBox_J,SIGNAL(valueChanged(double)), this, SLOT(updateStability()));
    connect(ui->doubleSpinBox_F,SIGNAL(valueChanged(double)), this, SLOT(updateStability()));
    connect(ui->doubleSpinBox_m,SIGNAL(valueChanged(double)), this, SLOT(updateStability()));
    connect(ui->doubleSpinBox_g,SIGNAL(valueChanged(double)), this, SLOT(updateStability()));
    this->updateStability();


}

MainWindow::~MainWindow()
{
    delete table;
    delete splineTable;
    delete ui;
}

bool MainWindow::event(QEvent *mEvent)
{
    if (mEvent->type()==QEvent::Close)
    {
        x_vDer->close();
        phasePortrait->close();
        difference->close();
        table->close();
        splineTable->close();
        this->close();
        return true;
    }
    return QMainWindow::event(mEvent);
}


void MainWindow::plotFunc2(QVector<double>& x, QVector<double>& y, QCustomPlot* holst, QPen color, QString xlabel, QString ylabel)
{
    //difference->clearGraphs();
    holst->addGraph();

    holst->graph(holst->graphCount()-1)->setData(x, y);
    // give the axes some labels:
    holst->xAxis->setLabel(xlabel);
    holst->yAxis->setLabel(ylabel);
    holst->graph(holst->graphCount()-1)->setPen(color);
    //ui->graphWidget->xAxis->setRange(x[0]-0.5,x[x.size()+1]+0.5);
    //ui->graphWidget->yAxis->setRange(-2.5,2.5);
    //ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->rescaleAxes(true);
    //difference->graph(0)->rescaleAxes();
    holst->replot();
}


vec3 MainWindow::fLab(double x, vec3 v)
{
    double k=ui->doubleSpinBox_k->value();
    double J=ui->doubleSpinBox_J->value();
    double F=ui->doubleSpinBox_F->value();
    double n=ui->doubleSpinBox_n->value();
    double g=ui->doubleSpinBox_g->value();
    double b=ui->doubleSpinBox_b->value();
    double m=ui->doubleSpinBox_m->value();

    return vec3(k/J*cos(v.t2)-F/J, v.t3, n*n*v.t1*v.t1*sin(v.t2)*cos(v.t2)-g*sin(v.t2)-b/m*v.t3);
}


void MainWindow::computeNextForSystemRK4(const double x, const vec3 v, const double h,
                                      vec3 & vNext)
{
    vec3 k1 = fLab(x,v);
    vec3 k2 = fLab(x+h/2.0,v+h/2.0*k1);
    vec3 k3 = fLab(x+h/2.0,v+h/2.0*k2);
    vec3 k4 = fLab(x+h,v+h*k3);
    vNext = v + h/6.0*(k1+2*k2+2*k3+k4);
}

void MainWindow::on_pushButton_clicked()
{

    //Steam Machine
    const int n = ui->spinBox_2->value();
    const int maxCounter = ui->spinBox_4->value();
    double h = 1.0/n;
    double a=0;
    const bool controlError = ui->radioButton_3->isChecked() ? false : true; // No or yes for controlling error

    bool t;
    const double eps = ui->lineEdit->text().toDouble(&t);

    const double u0 = ui->lineEdit_2->text().toDouble(&t);
    const double uDer0 = ui->lineEdit_3->text().toDouble(&t);
    const double omega0 = ui->lineEdit_4->text().toDouble(&t);

    vec3 vCurrent = vec3(omega0,u0,uDer0);
    vec3 vNext;


    QVector<double> x(maxCounter+1), localErrorEst(maxCounter+1);
    QVector<double> v1(maxCounter+1), v2(maxCounter+1), v3(maxCounter+1);

    int* c1 = new int[maxCounter+1];
    int* c2 = new int[maxCounter+1];
    int sumC1=0, sumC2=0;

    x[0]=a;
    localErrorEst[0]=0;
    v1[0]=vCurrent[1];
    v2[0]=vCurrent[2];
    v3[0]=vCurrent[3];
    memset(c1,0,sizeof(int)*(maxCounter+1));
    memset(c2,0,sizeof(int)*(maxCounter+1));

    int counter=1;
    double T = ui->doubleSpinBox->value();
    if (T==0)
        T=1e308;
    double epsForRightBound=ui->lineEdit_5->text().toDouble(&t);
    double maxLocalError = -1;
    double maxH=-1, minH=1e6, maxHX, minHX;

    do
    {
        x[counter] = x[counter-1]+h;

        computeNextForSystemRK4(x[counter-1], vCurrent, h, vNext);

        vec3 temp, secondPoint;

        computeNextForSystemRK4(x[counter-1], vCurrent, h/2.0, temp);
        computeNextForSystemRK4(x[counter-1], temp, h/2.0, secondPoint);

        vec3 s = secondPoint-vNext;
        s = 1.0/15.0*s;  //p==4
        localErrorEst[counter] = s.norm();
        if (controlError)
        {
            if (fabs(localErrorEst[counter])<eps/32.0)
            {
                h*=2.0;
                c2[counter]++;
                sumC2++;
            }
            else if (fabs(localErrorEst[counter])>eps)
            {
                h/=2.0;
                c1[counter]++;
                sumC1++;
                continue;
            }
        }

        v1[counter]=vNext[1];
        v2[counter]=vNext[2];
        v3[counter]=vNext[3];

        vCurrent = vNext;

        if (fabs(localErrorEst[counter])>maxLocalError)
            maxLocalError=fabs(localErrorEst[counter]);
        if (h>maxH)
        {
            maxH=h;
            maxHX=x[counter];
        }
        if (h<minH)
        {
            minH=h;
            minHX=x[counter];
        }
        counter++;
    } while (x[counter-1]<(T-epsForRightBound) && counter <= maxCounter);

    QMessageBox msgBox;
    //msgBox.setText(QString("Was done %1 iterations.\n Accuracy %2\nMax diff |u-v| %3 in (i,j)=(%4,%5)").arg(counter).arg(QString::number(currentEps, 'e', 5)).arg(maxDiff).arg(maxI).arg(maxJ));
    //msgBox.exec();

    ui->label_6->setText(QString("Was done %1 iterations").arg(counter-1));
    ui->label_7->setText(QString("Total divisions %1; total doubling %2").arg(QString::number(sumC1)).arg(sumC2));
    ui->label_8->setText(QString("Max S = %1").arg(QString::number(maxLocalError, 'e', 5)));
    ui->label_9->setText(QString("Max h = %1 in x= %2").arg(maxH).arg(maxHX));
    ui->label_11->setText(QString("Min h = %1 in x= %2").arg(minH).arg(minHX));
    ui->label_2->setText(QString("Max Max local error est. = %1").arg(QString::number(maxLocalError*16, 'e', 5)));
    //ui->label_2->setText(QString("Max |u-v| = %1 in x= %2").arg(maxDiff).arg(maxDiffX));


    phasePortrait->clearGraphs();
    plotFunc2(v2,v3, phasePortrait, QPen(Qt::red), "fi", "fi'");
    phasePortrait->graph(0)->setLineStyle(QCPGraph::lsNone);
    phasePortrait->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
    phasePortrait->graph(0)->rescaleAxes();
    phasePortrait->replot();

    difference->clearGraphs();
    plotFunc2(v2,v1, difference, QPen(Qt::blue), "fi", "omega");
    difference->graph(0)->setLineStyle(QCPGraph::lsNone);
    difference->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
    difference->graph(0)->rescaleAxes();
    difference->replot();

    x_vDer->clearGraphs();
    plotFunc2(x,v1, x_vDer, QPen(Qt::green));
    plotFunc2(x,v2, x_vDer, QPen(Qt::blue));
    plotFunc2(x,v3, x_vDer, QPen(Qt::red));
    x_vDer->graph(0)->setName("omega");
    x_vDer->graph(1)->setName("fi");
    x_vDer->graph(2)->setName("fi'");
    x_vDer->graph(2)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
    x_vDer->graph(0)->rescaleAxes();
    x_vDer->graph(1)->rescaleAxes(true);
    x_vDer->graph(2)->rescaleAxes(true);
    x_vDer->legend->setVisible(true);
    x_vDer->replot();

    x_vDer->show();
    phasePortrait->show();

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        table->setColumnCount(8);
        table->setRowCount(counter);
        table->setWindowTitle("Steam machine task");
        QStringList headers;
        for (int i=0; i<counter; i++)
            headers.append(QString::number(i));
        table->setVerticalHeaderLabels(headers);
        QStringList vheaders;
        vheaders.append("x_i");
        vheaders.append("omega_i");
        vheaders.append("fi_i");
        vheaders.append("fi_der_i");
        vheaders.append("S");
        vheaders.append("h_i");
        vheaders.append("C1");
        vheaders.append("C2");
        table->setHorizontalHeaderLabels(vheaders);
        //table->verticalHeader()->hide();
        //table->setColumnWidth(0,35);


        for (int i=0; i<counter; i++)
        {
            double H = 1.0/n;
            if (i>0)
                H = x[i]-x[i-1];
            table->setItem(i,0,new QTableWidgetItem(QString::number(x[i],'g',4)));
            table->setItem(i,1,new QTableWidgetItem(QString::number(v1[i],'g',4)));
            table->setItem(i,2,new QTableWidgetItem(QString::number(v2[i],'g',4)));
            table->setItem(i,3,new QTableWidgetItem(QString::number(v3[i],'g',4)));
            table->setItem(i,4,new QTableWidgetItem(QString::number(localErrorEst[i],'g',4)));
            table->setItem(i,5,new QTableWidgetItem(QString::number(H,'g',4)));
            table->setItem(i,6,new QTableWidgetItem(QString::number(c1[i])));
            table->setItem(i,7,new QTableWidgetItem(QString::number(c2[i])));
        }
        table->show();

    }

    delete[] c1;
    delete[] c2;
}

void MainWindow::updateStability()
{
    double k=ui->doubleSpinBox_k->value();
    double J=ui->doubleSpinBox_J->value();
    double F=ui->doubleSpinBox_F->value();
    double n=ui->doubleSpinBox_n->value();
    double g=ui->doubleSpinBox_g->value();
    double b=ui->doubleSpinBox_b->value();
    double m=ui->doubleSpinBox_m->value();

    double omegaStar = sqrt(g*k/(F*n*n));
    double nu = omegaStar/(2*F);
    double res = b*J*nu/m;
    ui->label_20->setText(QString("bJnu/m = %1 > 1").arg(res));
}
